package c2.cross.business.jungle;

import c2.cross.business.CrossBody;
import c2.cross.business.base.CrossModule;
import c2.cross.business.jungle.data.JungleMainKV;
import cate.common.table.jungle.buff.JungleBuffRow;
import cate.common.table.jungle.task.JungleTaskRow;
import cate.common.util.GameResult;
import cate.common.util.TimeTool;
import cate.game.chat.ChatContentParam;
import cate.game.db.mongo.kv.KvMongo;
import cate.game.play.part.PlaySide;
import cate.game.pvp.jungle.JungleConstants;
import cate.game.pvp.jungle.core.*;
import cate.game.pvp.jungle.msg.*;
import cate.game.res.MixRes;
import cate.game.role.RoleSnapshot;
import cp.solution.rpc.RpcMsgHead;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

import static cate.game.chat.ChatContentParam.TYPE_STRING;

@Slf4j
@NoteClass("丛林冒险跨服模块")
public class JungleModule extends CrossModule<JungleMainKV> {
    @NoteField("任务锁")
    private final ReentrantLock taskLock = new ReentrantLock();
    @NoteField("BUFF锁")
    private final ReentrantLock buffLock = new ReentrantLock();
    @NoteField("下次任务结束时间")
    private long nextTaskEndTime;

    public JungleModule(CrossBody context) {
        super(context);
    }

    @Override
    protected String getStatusKey() {
        return KvMongo.KEY_CROSS_JUNGLE;
    }

    @Override
    public String getName() {
        return "丛林冒险";
    }

    @Override
    public void initDependent() throws Exception {
        super.initDependent();
        // 加载常量
        JungleConstants.loadFromTable(table().common);
        // 数据加载处理
        status.tasks.values().forEach(taskData -> {
            if (taskData.lock == null) {
                taskData.lock = new ReentrantLock();
            }
        });
        // 加载一下最新的任务检测时间
        loadNextTaskEndTime();
        // 监听消息
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.VIEW_OVERALL, Void.class, this::viewOverallInfo);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.VIEW_DETAIL, String.class, this::viewDetailInfo);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.VIEW_BUFF, Void.class, this::viewBuffs);
        rpc().recv().onFireAndForgetTemplate(JungleInnerMsg.TRIGGER_BUFF, RoleSnapshot.class, this::triggerBuff);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.MY_CURRENT_TASK, String.class, this::getRoleTask);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.START_TASK, JungleStartTaskReq.class, this::startTask);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.SUPPORT_BUSY, JungleSupportUidList.class, this::supportBusy);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.COST_SPEED_UP, String.class, this::speedUp);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.ROB_TASK, JungleRobReq.class, this::robTask);
        rpc().recv().onRequestResponseTemplate(JungleInnerMsg.ROB_RESULT_REPORT, JungleRobReportReq.class, this::reportRobResult);
    }

    @Override
    protected int getIntervalSeconds() {
        return 1;
    }

    @Override
    protected void onInterval() {
        long nowTime = System.currentTimeMillis();
        // 检测BUFF过期
        boolean broadcast_buff = false;
        buffLock.lock();
        try {
            if (status.buffs.removeIf(buff -> buff.buffEndTime <= nowTime)) {
                broadcast_buff = true;
            }
        } finally {
            buffLock.unlock();
        }
        // 检测固定时间点刷新BUFF
        boolean stableCheck = checkAddStableTimeBuff();
        broadcast_buff = broadcast_buff || stableCheck;
        if (broadcast_buff) {
            broadcastBuffs();
        }
        // 检测掠夺锁定打点
        status.tasks.values().stream()
                .filter(taskData -> taskData.robLockTick > 0)
                .forEach(taskData -> taskData.robLockTick--);
        // 检测任务完成
        if (nextTaskEndTime > 0 && nextTaskEndTime <= nowTime) {
            taskLock.lock();
            try {
                Iterator<Map.Entry<String, JungleTaskData>> itr = status.tasks.entrySet().iterator();
                while (itr.hasNext()) {
                    Map.Entry<String, JungleTaskData> entry = itr.next();
                    if (entry.getValue().endTime <= nowTime) {
                        handleTaskEnd(entry.getValue());
                        itr.remove();
                    }
                }
                loadNextTaskEndTime();
            } finally {
                broadcastOverall();
                taskLock.unlock();
            }
        }
    }

    /**
     * 检测是否添加固定时间BUFF
     *
     * @return 是否确实添加了BUFF
     */
    private boolean checkAddStableTimeBuff() {
        List<JungleBuffRow> list = table().jungle.buff.getList().stream()
                .filter(buffRow -> buffRow.buffType == JungleConstants.BUFF_TYPE_AUTO && status.buffs.stream().noneMatch(buff -> buff.buffId == buffRow.id))
                .collect(Collectors.toList());
        if (list.isEmpty()) {
            return false;
        }
        boolean changed = false;
        long nowTime = System.currentTimeMillis();
        long dayTime = TimeTool.getZeroTimeStamp(nowTime);
        for (JungleBuffRow buffRow : list) {
            if (getBuffParamMax(buffRow.buffEffect) > 0.0D) {
                continue;
            }
            if (buffRow.stableAddHours == null) {
                continue;
            }
            for (int hour : buffRow.stableAddHours) {
                long startTime = dayTime + hour * TimeTool.TimeCons.HOUR;
                long endTime = startTime + buffRow.buffTimeSec * TimeTool.TimeCons.SECOND;
                if (nowTime >= startTime && nowTime < endTime) {
                    addBuff(buffRow);
                    changed = true;
                    break;
                }
            }
        }
        return changed;
    }

    /**
     * 加载下一个任务结束时间
     */
    private void loadNextTaskEndTime() {
        nextTaskEndTime = status.tasks.values().stream().mapToLong(task -> task.endTime).min().orElse(-1);
    }

    /**
     * 获取BUFF配置
     *
     * @param buffId BUFF配置ID
     */
    private JungleBuffRow getBuffRow(int buffId) {
        return table().jungle.buff.get(buffId);
    }

    /**
     * BUFF生效最大值
     *
     * @param buffEffect BUFF效果
     * @return 不生效时返回0
     */
    private double getBuffParamMax(int buffEffect) {
        return status.buffs.stream()
                .filter(buff -> Optional.ofNullable(getBuffRow(buff.buffId))
                        .map(row -> row.buffEffect == buffEffect).orElse(false))
                .mapToDouble(buff -> getBuffRow(buff.buffId).effectParam)
                .max()
                .orElse(0.0D);
    }

    /**
     * BUFF生效效果列
     *
     * @param buffEffect BUFF效果
     */
    private List<Integer> getBuffParams(int buffEffect) {
        return status.buffs.stream()
                .filter(buff -> Optional.ofNullable(getBuffRow(buff.buffId))
                        .map(row -> row.buffEffect == buffEffect).orElse(false))
                .map(buff -> (int) getBuffRow(buff.buffId).effectParam)
                .collect(Collectors.toList());
    }

    /**
     * 添加BUFF
     *
     * @param buffRow BUFF配置
     */
    private void addBuff(JungleBuffRow buffRow) {
        buffLock.lock();
        try {
            Optional<JungleBuff> op = status.buffs.stream()
                    .filter(buff -> buff.buffId == buffRow.id)
                    .findAny();
            if (op.isPresent()) {
                JungleBuff buff = op.get();
                buff.buffEndTime = Math.max(buff.buffEndTime, System.currentTimeMillis() + buffRow.buffTimeSec * TimeTool.TimeCons.SECOND);
            } else {
                JungleBuff buff = new JungleBuff();
                buff.buffId = buffRow.id;
                buff.buffEndTime = System.currentTimeMillis() + buffRow.buffTimeSec * TimeTool.TimeCons.SECOND;
                status.buffs.add(buff);
            }
        } finally {
            buffLock.unlock();
        }
    }

    /**
     * 处理任务完结
     *
     * @param taskData 任务数据
     */
    private void handleTaskEnd(JungleTaskData taskData) {
        JungleFinishReward fin = new JungleFinishReward(taskData.taskId, taskData.reward);
        JungleFinishEvent event = new JungleFinishEvent(taskData.owner.base.srvId, taskData.owner.uid, fin);
        try {
            rpc().send().fireAndForgetGameTemplate(JungleInnerMsg.TASK_FINISH_EVENT, taskData.owner.base.srvId, event);
        } catch (Exception e) {
            log.error("发送任务完结事件失败! 事件:[{}]", event);
            log.error("", e);
        }
        if (taskData.supporter != null) {
            status.supports.remove(taskData.supporter.uid);
        }
    }

    /**
     * 获取玩家的任务
     *
     * @param head    消息头
     * @param roleUid 玩家唯一ID
     * @return 玩家对应任务
     */
    private JungleTaskData getRoleTask(RpcMsgHead head, String roleUid) {
        return status.tasks.get(roleUid);
    }

    /**
     * 查看总体数据
     *
     * @param head 消息头
     * @param v    虚无
     * @return 总体数据
     */
    private JungleOverallVO viewOverallInfo(RpcMsgHead head, Void v) {
        return new JungleOverallVO(new ArrayList<>(status.tasks.values()));
    }

    /**
     * 查看任务具体信息
     *
     * @param head      消息头
     * @param targetUid 目标唯一ID
     * @return 具体信息
     */
    private JungleTaskDetailVO viewDetailInfo(RpcMsgHead head, String targetUid) {
        JungleTaskData taskData = status.tasks.get(targetUid);
        if (taskData == null) {
            return null;
        }
        // 加载阵容
        PlaySide playSide = help().getAdventure(taskData.supported ? taskData.supporter.uid : taskData.owner.uid);
        return new JungleTaskDetailVO(taskData, playSide);
    }

    /**
     * 查看BUFF
     *
     * @param head 消息头
     * @param v    虚无
     * @return BUFF信息
     */
    private JungleBuffVO viewBuffs(RpcMsgHead head, Void v) {
        return new JungleBuffVO(new ArrayList<>(status.buffs));
    }

    /**
     * 广播概览变更
     */
    private void broadcastOverall() {
        try {
            rpc().send().fireAndForgetGameInCrossTemplate(JungleInnerMsg.BROADCAST_OVERALL,
                    context().getCode(),
                    new JungleOverallVO(new ArrayList<>(status.tasks.values())));
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 广播BUFF变更
     */
    private void broadcastBuffs() {
        try {
            rpc().send().fireAndForgetGameInCrossTemplate(JungleInnerMsg.BROADCAST_BUFF,
                    context().getCode(),
                    new JungleBuffVO(new ArrayList<>(status.buffs)));
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 开始任务
     *
     * @param head 消息头
     * @param req  请求消息
     * @return 结果
     */
    private JungleStartTaskRet startTask(RpcMsgHead head, JungleStartTaskReq req) {
        return new JungleStartTaskRet(startTaskResult(req), status.tasks.get(req.owner.uid));
    }

    /**
     * 开启任务
     *
     * @param req 请求消息
     * @return 结果
     */
    private GameResult<Void> startTaskResult(JungleStartTaskReq req) {
        GameResult<Void> r = new GameResult<>();
        taskLock.lock();
        try {
            if (status.tasks.containsKey(req.owner.uid)) {
                return r.fail("冒险任务进行中,不可重复开启!");
            }
            if (req.supporter != null && status.supports.containsKey(req.supporter.uid)) {
                return r.fail("协助角色当前忙碌");
            }
            JungleTaskRow taskRow = table().jungle.task.get(req.taskId);
            if (taskRow == null) {
                return r.fail("冒险任务不存在");
            }
            if (req.supporter != null && help().getAdventure(req.supporter.uid) == null) {
                return r.fail("协助玩家未开启玩法");
            }
            JungleTaskData taskData = new JungleTaskData();
            taskData.owner = req.owner;
            taskData.startTime = System.currentTimeMillis();
            int reduceSec = (int) getBuffParamMax(JungleConstants.BUFF_EFFECT_REDUCE_TASK_TIME);
            taskData.endTime = taskData.startTime + (taskRow.taskTimeSec - reduceSec) * TimeTool.TimeCons.SECOND;
            taskData.taskId = req.taskId;
            taskData.reward = new MixRes(taskRow.taskRewardStr);
            double multi = getBuffParamMax(JungleConstants.BUFF_EFFECT_MULTIPLE_REWARD);
            if (multi > 0) {
                taskData.reward.multiplyDouble(multi);
            }
            taskData.robbedTimes = 0;
            taskData.supported = req.supporter != null;
            taskData.supporter = req.supporter;
            status.tasks.put(req.owner.uid, taskData);
            if (req.supporter != null) {
                status.supports.put(req.supporter.uid, true);
            }
            loadNextTaskEndTime();
            return r.success();
        } finally {
            broadcastOverall();
            taskLock.unlock();
        }
    }

    /**
     * 支援忙碌列表
     *
     * @param head 消息头
     * @param list 原始UID列表
     * @return 忙碌UID列表
     */
    private JungleSupportUidList supportBusy(RpcMsgHead head, JungleSupportUidList list) {
        return new JungleSupportUidList(list.uidList.stream()
                .filter(uid -> status.supports.containsKey(uid))
                .collect(Collectors.toList()));
    }

    /**
     * 任务加速
     *
     * @param head    消息头
     * @param roleUid 玩家唯一ID
     * @return 是否加速成功
     */
    private Boolean speedUp(RpcMsgHead head, String roleUid) {
        taskLock.lock();
        try {
            JungleTaskData taskData = status.tasks.remove(roleUid);
            if (taskData == null) {
                return false;
            }
            handleTaskEnd(taskData);
            loadNextTaskEndTime();
            return true;
        } finally {
            taskLock.unlock();
            broadcastOverall();
        }
    }

    /**
     * 发起掠夺
     *
     * @param head 消息头
     * @param req  请求数据
     * @return 掠夺结果
     */
    private JungleRobRet robTask(RpcMsgHead head, JungleRobReq req) {
        JungleRobRet ret = new JungleRobRet();
        ret.r = new GameResult<>();
        JungleTaskData taskData = status.tasks.get(req.targetUid);
        if (taskData == null) {
            ret.r.fail("冒险任务不存在");
            return ret;
        }
        if (taskData.robbedTimes >= JungleConstants.TASK_ROB_MAX) {
            ret.r.fail("该玩家已经无法被掠夺");
            return ret;
        }
        if (taskData.robLockTick > 0) {
            ret.r.fail("系统忙,请稍后");
            return ret;
        }
        ret.playSide = help().getAdventure(taskData.supported ? taskData.supporter.uid : taskData.owner.uid);
        if (ret.playSide == null) {
            ret.r.fail("阵容丢失!");
            return ret;
        }
        ReentrantLock lock = taskData.lock;
        lock.lock();
        try {
            taskData.robLockTick = 5;
        } finally {
            lock.unlock();
        }
        ret.opponent = taskData.owner.copy();
        ret.attackBuffs = getBuffParams(JungleConstants.BUFF_EFFECT_ATTACK_BUFF);
        ret.defendBuffs = getBuffParams(JungleConstants.BUFF_EFFECT_DEFEND_BUFF);
        return ret;
    }

    /**
     * 上报掠夺结果
     *
     * @param head 消息头
     * @param req  请求消息
     * @return 上报结果
     */
    private JungleRobReportRet reportRobResult(RpcMsgHead head, JungleRobReportReq req) {
        JungleRobReportRet ret = new JungleRobReportRet();
        ret.r = new GameResult<>();
        JungleTaskData taskData = status.tasks.get(req.targetUid);
        if (taskData == null) {
            ret.r.fail("冒险任务不存在");
            return ret;
        }
        JungleTaskRow taskRow = table().jungle.task.get(taskData.taskId);
        if (taskRow == null) {
            ret.r.fail("冒险任务不存在");
            return ret;
        }
        if (taskData.robbedTimes >= JungleConstants.TASK_ROB_MAX) {
            ret.r.fail("该玩家已经无法被掠夺");
            return ret;
        }
        ReentrantLock lock = taskData.lock;
        lock.lock();
        try {
            taskData.robLockTick = 0;
            if (req.win) {
                ret.reward = new MixRes(taskRow.robRewardStr);
                taskData.reward.minusList(ret.reward);
                taskData.robbedTimes++;
                sendTaskChange(taskData);
            }
        } finally {
            lock.unlock();
        }
        broadcastOverall();
        long nowTime = System.currentTimeMillis();
        ret.report = new JungleRobReport(nowTime, JungleConstants.REPORT_TYPE_ROB,
                req.win, taskData.owner, ret.reward);
        JungleRobReport reportTarget = new JungleRobReport(nowTime, JungleConstants.REPORT_TYPE_DEFEND,
                !req.win, req.self, ret.reward);
        addRobReport(taskData.owner, reportTarget);
        return ret;
    }

    /**
     * 发送任务数据变更
     *
     * @param taskData 任务数据
     */
    private void sendTaskChange(JungleTaskData taskData) {
        try {
            rpc().send().fireAndForgetGameTemplate(JungleInnerMsg.TASK_CHANGE, taskData.owner.base.srvId, taskData);
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 为被掠夺方添加战报
     *
     * @param owner  被掠夺方
     * @param report 战报
     */
    private void addRobReport(RoleSnapshot owner, JungleRobReport report) {
        try {
            rpc().send().fireAndForgetGameTemplate(JungleInnerMsg.ADD_ROB_REPORT, owner.base.srvId, new JungleAddRobReport(owner.uid, report));
        } catch (Exception e) {
            log.error("", e);
        }
    }

    /**
     * 刷新任务触发BUFF
     *
     * @param head     消息头
     * @param snapshot 触发者快照
     */
    private void triggerBuff(RpcMsgHead head, RoleSnapshot snapshot) {
        List<JungleBuffRow> list = table().jungle.buff.getList().stream()
                .filter(buffRow -> buffRow.buffType == JungleConstants.BUFF_TYPE_RARE)
                .collect(Collectors.toList());
        if (list.size() == 0) {
            return;
        }
        Collections.shuffle(list);
        JungleBuffRow buffRow = list.get(0);
        addBuff(buffRow);
        broadcastBuffs();
        help().broadcastLampNotice(JungleConstants.RARE_LAMB_ID, ChatContentParam.c(TYPE_STRING, snapshot.base.name));
    }
}